#include "./myTools.hpp"
#include "./myConst.hpp"
//#pragma comment(lib,"glaux.lib")
database_mt DataBase;

void init( void ) {
    glClearColor( background_r, background_g, background_b, background_a );

    glOrtho( or_left, or_right, or_bottom, or_top, or_near, or_far );
    glMatrixMode( GL_PROJECTION );
    glLoadIdentity();
    //gluPerspective( per_fovy, per_aspect, per_zNear, per_zFar );
    glMatrixMode( GL_MODELVIEW );
    gluLookAt( x0, y0, z0, xref, yref, zref, vx, vy, vz );
    glViewport( 0, 0, ( GLsizei )winWidth, ( GLsizei )winHeight );

#ifdef MATERIAL
    glLightfv( GL_LIGHT0, GL_POSITION, light_pos );
    glLightfv( GL_LIGHT0, GL_AMBIENT, light_ambient );
    glLightfv( GL_LIGHT0, GL_SPECULAR, light_specular );
    glLightfv( GL_LIGHT0, GL_DIFFUSE, light_diffuse );
    glEnable( GL_LIGHTING );
    glEnable( GL_LIGHT0 );
    glEnable( GL_DEPTH_TEST );
    glDepthFunc( GL_LEQUAL );
#endif // MATERIAL
    glCullFace( GL_BACK );
    glEnable( GL_CULL_FACE );
    glEnable( GL_TEXTURE_2D );
    glShadeModel( GL_SMOOTH );
    loadTextureFile();
}

void reshape( GLint width, GLint height ) {

    glViewport( 0, 0, width, height );

    //glLoadIdentity();
    //or_rate = ((GLdouble)width)/height;
    //glOrtho( or_left, or_right, or_rate*or_left, or_rate*or_right, or_near, or_far );
    //winWidth = width;
    //winHeight = height;
    //glViewport( 0, 0, ( GLsizei )width, ( GLsizei )height );
}

void drawAxis( void ) {
    glLineWidth( 2.0 );
    glPointSize( 6.0 );
    // x axis
    glBegin( GL_LINES );
    glColor3f( 1.0, 0.0, 0.0 );
    glVertex3f( 0, 0, 0 );
    glVertex3f( 2, 0, 0 );
    glEnd();
    glBegin( GL_POINTS );
    glVertex3f( 1.0, 0.0, 0.0 );
    glEnd();
    // y axis
    glBegin( GL_LINES );
    glColor3f( 0.0, 1.0, 0.0 );
    glVertex3f( 0, 0, 0 );
    glVertex3f( 0, 2, 0 );
    glEnd();
    glBegin( GL_POINTS );
    glVertex3f( 0.0, 1.0, 0.0 );
    glEnd();
    // z axis
    glBegin( GL_LINES );
    glColor3f( 0.0, 0.0, 1.0 );
    glVertex3f( 0, 0, 0 );
    glVertex3f( 0, 0, 2 );
    glEnd();
    glBegin( GL_POINTS );
    glVertex3f( 0.0, 0.0, 1.0 );
    glEnd();
}

void vertexMOD( void ) {
    vertex_coordinate_mt* v_set = nullptr;
    int num = DataBase.Num_vertex;
    v_set = DataBase.Vertex_Set;
    glClear( GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT );
    glDisable( GL_LIGHTING );

    glColor3f( r_vmod, g_vmod, b_vmod );
    glPointSize( Psize_vmod );
    glBegin( GL_POINTS );
    for( int i = 0; i < num; i += 1 ) {
        glVertex3fv( v_set[i] );
    }
    glEnd();
    drawAxis();
    glFlush();
}

void lineMOD( void ) {

    submodel_mt* models = DataBase.Submodel_Set;
    submodel_triangle_mt* triangle;
    vertex_coordinate_mt *v_set = DataBase.Vertex_Set;
    int P1, P2, P3, i, j;

    glClear( GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT );
    glDisable( GL_LIGHTING );

    glColor3f( Pr_lmod, Pg_lmod, Pb_lmod );
    glPointSize( Psize_lmod );
    glBegin( GL_POINTS );
    for( i = 0; i < DataBase.Num_vertex; i += 1 ) {
        glVertex3fv( v_set[i] );
    }
    glEnd();


    glColor3f( r_lmod, g_lmod, b_lmod );
    glPolygonMode( GL_FRONT_AND_BACK, GL_LINE );
    glLineWidth( Lwidth_lmod );
    glBegin( GL_TRIANGLES );
    for( i = 0; i < DataBase.Num_submodel; i++ ) {
        triangle = models[i].triangles;
        for( j = 0; j < models[i].triangleNumber; j++ ) {
            P1 = triangle[j][0] - 1;
            P2 = triangle[j][3] - 1;
            P3 = triangle[j][6] - 1;
            glVertex3fv( v_set[P1] );
            glVertex3fv( v_set[P2] );
            glVertex3fv( v_set[P3] );
        }
    }
    glEnd();
    drawAxis();
    glFlush();
}

void PictureMod( void ) {
    vertex_coordinate_mt* v_set = DataBase.Vertex_Set;
    submodel_mt *models = DataBase.Submodel_Set;
    submodel_triangle_mt *triangle_set = nullptr;
    chartlet_coordinate_mt *chartlets = DataBase.Chartlet_Set;
    normal_coordinate_mt *normals = DataBase.Normal_Dirc_Set;
    int material_No = -1, P1, P2, P3, T1, T2, T3, N1, N2, N3;
    material_mt* p_material = nullptr;
    int i, j;

    glClear( GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT );
    glEnable( GL_LIGHTING );
    //glColor3fv( color_pmod );
    glPolygonMode( GL_FRONT_AND_BACK, GL_FILL );
    for( i = 0; i < DataBase.Num_submodel; i++ ) {
        triangle_set = models[i].triangles;
        material_No = models[i].materialIndex - 1;
        p_material = &( DataBase.Material_Set[material_No] );

#ifdef MATERIAL
        glMaterialfv( GL_FRONT, GL_AMBIENT, p_material->ambient );
        glMaterialfv( GL_FRONT, GL_DIFFUSE, p_material->diffuse );
        glMaterialfv( GL_FRONT, GL_SPECULAR, p_material->specular );
        glMaterialf( GL_FRONT, GL_SHININESS, p_material->shininess );
        glMaterialfv( GL_FRONT, GL_EMISSION, p_material->emission );
        //glActiveTexture(p_material->texture);
#endif // MATERIAL
        glBindTexture( GL_TEXTURE_2D, p_material->texture );

        glBegin( GL_TRIANGLES );
        for( j = 0; j < models[i].triangleNumber; j++ ) {
            P1 = triangle_set[j][0] - 1;
            T1 = triangle_set[j][1] - 1;
            N1 = triangle_set[j][2] - 1;
            P2 = triangle_set[j][3] - 1;
            T2 = triangle_set[j][4] - 1;
            N2 = triangle_set[j][5] - 1;
            P3 = triangle_set[j][6] - 1;
            T3 = triangle_set[j][7] - 1;
            N3 = triangle_set[j][8] - 1;

            glNormal3fv( normals[N1] );
            glTexCoord2fv( chartlets[T1] );
            glVertex3fv( v_set[P1] );

            glNormal3fv( normals[N2] );
            glTexCoord2fv( chartlets[T2] );
            glVertex3fv( v_set[P2] );

            glNormal3fv( normals[N3] );
            glTexCoord2fv( chartlets[T3] );
            glVertex3fv( v_set[P3] );
        }// j
        glEnd();
    }// i
    glDisable( GL_LIGHTING );
    drawAxis();
    glFlush();
}

void chooseDisplayMod( void ) {
    switch( display_mod ) {
    case 0:
        vertexMOD();
        break;
    case 1:
        lineMOD();
        break;
    case 2:
        PictureMod();
        break;
    default:
        lineMOD();
        break;
    }
}

void mouseFcn( GLint botton, GLint action, GLint xMouse, GLint yMouse ) {
    if( botton == GLUT_LEFT_BUTTON && action == GLUT_UP ) {
        scale = scale * ( 1.0 + scale_step );
        glScalef( 1 + scale_step, 1 + scale_step, 1 + scale_step );
    } else if( botton == GLUT_RIGHT_BUTTON && action == GLUT_UP ) {
        scale = scale / ( 1.0 + scale_step );
        glScalef( 1.0 / ( 1 + scale_step ), 1.0 / ( 1 + scale_step ), 1.0 / ( 1 + scale_step ) );
    }
#ifdef DEBUG
    std::cout << "mouseFunc:botton:" << botton << "\taction:" << action << std::endl;
    std::cout << "scale=" << scale << std::endl;
    std::cout << "x0\ty0\tz0\txref\tyref\tzref" << std::endl;
    std::cout << x0 << "\t" << y0 << "\t" << z0 << "\t" << xref << "\t" << yref << "\t" << zref << std::endl;
#endif // DEBUG
    glFlush();
}

void keyFcn( GLubyte key, GLint xMouse, GLint yMouse ) {
    switch( key ) {
    case '+':
        scale = scale * ( 1 + scale_step );
        glScalef( 1 + scale_step, 1 + scale_step, 1 + scale_step );
        break;
    case '-':
        scale = scale / ( 1.0 + scale_step );
        glScalef( 1.0 / ( 1 + scale_step ), 1.0 / ( 1 + scale_step ), 1.0 / ( 1 + scale_step ) );
        break;
    case 'a':
        rotate_theta[2] += rotate_angle_step;
        rotate_theta[2] = ( rotate_theta[2] >= 360 ) ? ( rotate_theta[2] - 360 ) : rotate_theta[2];
        glRotatef( rotate_angle_step, 0.0, 0.0, 1.0 );
        break;
    case 'd':
        rotate_theta[2] -= rotate_angle_step;
        rotate_theta[2] = ( rotate_theta[2] < 0 ) ? ( rotate_theta[2] + 360 ) : rotate_theta[2];
        glRotatef( -rotate_angle_step, 0.0, 0.0, 1.0 );
        break;
    case 'w':
        rotate_theta[0] += rotate_angle_step;
        rotate_theta[0] = ( rotate_theta[0] >= 360 ) ? ( rotate_theta[0] - 360 ) : rotate_theta[0];
        glRotatef( rotate_angle_step, 1.0, 0.0, 0.0 );
        break;
    case 's':
        rotate_theta[0] -= rotate_angle_step;
        rotate_theta[0] = ( rotate_theta[0] < 0 ) ? ( rotate_theta[0] + 360 ) : rotate_theta[0];
        glRotatef( -rotate_angle_step, 1.0, 0.0, 0.0 );
        break;
    case 'W':
        rotate_theta[1] += rotate_angle_step;
        rotate_theta[1] = ( rotate_theta[1] >= 360 ) ? ( rotate_theta[1] - 360 ) : rotate_theta[1];
        glRotatef( rotate_angle_step, 0.0, 1.0, 0.0 );
        break;
    case 'S':
        rotate_theta[1] -= rotate_angle_step;
        rotate_theta[1] = ( rotate_theta[1] < 0 ) ? ( rotate_theta[1] + 360 ) : rotate_theta[1];
        glRotatef( -rotate_angle_step, 0.0, 1.0, 0.0 );
        break;
    case 27u:
        exit( 0 );
        break;
    default:
        ;
    }
#ifdef DEBUG
    //std::cout << "Keyboard get:" << key << std::endl;
    std::cout << "rotate_theta:" << rotate_theta[0] << '\t' << rotate_theta[1] << '\t' << rotate_theta[2] << std::endl;
#endif // DEBUG
    glFlush();
}

void keyFcn_special( GLint key, GLint x, GLint y ) {
    switch( key ) {
    case GLUT_KEY_F1:
        display_mod = 0;
        break;
    case GLUT_KEY_F2:
        display_mod = 1;
        break;
    case GLUT_KEY_F3:
        display_mod = 2;
        break;
    case GLUT_KEY_UP:
        glTranslatef( translate_step[2][0], translate_step[2][1], translate_step[2][2] );
        break;
    case GLUT_KEY_DOWN:
        glTranslatef( -translate_step[2][0], -translate_step[2][1], -translate_step[2][2] );
        break;
    case GLUT_KEY_LEFT:
        if( glutGetModifiers() == GLUT_ACTIVE_CTRL )
            glTranslatef( -translate_step[1][0], -translate_step[1][1], -translate_step[1][2] );
        else
            glTranslatef( -translate_step[0][0], -translate_step[0][1], -translate_step[0][2] );
        break;
    case GLUT_KEY_RIGHT:
        if( glutGetModifiers() == GLUT_ACTIVE_CTRL )
            glTranslatef( translate_step[1][0], translate_step[1][1], translate_step[1][2] );
        else
            glTranslatef( translate_step[0][0], translate_step[0][1], translate_step[0][2] );
        break;
    default:
        break;
    }
}

/* Program entry point */
int main( int argc, char *argv[] ) {
    glutInit( &argc, argv );
    glutInitDisplayMode( GLUT_DEPTH | GLUT_SINGLE | GLUT_RGB );
    glutInitWindowPosition( winPositionx, winPositiony );
    glutInitWindowSize( winWidth, winHeight );
    glutCreateWindow( "Luweiqi" );
    mygetData();

    init();
    glutDisplayFunc( chooseDisplayMod );
    glutIdleFunc( chooseDisplayMod );
    glutReshapeFunc( reshape );
    glutMouseFunc( mouseFcn );
    glutKeyboardFunc( keyFcn );
    glutSpecialFunc( keyFcn_special );

    glutMainLoop();
    return EXIT_SUCCESS;
}
